package com.zzgele.qmzb.api.configurer;

import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.annotation.Resource;
import javax.servlet.MultipartConfigElement;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.alibaba.boot.dubbo.annotation.DubboConsumer;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.MultipartConfigFactory;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter4;
import com.zzgele.qmzb.api.core.Constants;
import com.zzgele.qmzb.api.core.Result;
import com.zzgele.qmzb.api.core.ResultCode;
import com.zzgele.qmzb.api.core.ServiceException;
import com.zzgele.qmzb.api.model.User;
import com.zzgele.qmzb.api.service.UserService;
import com.zzgele.qmzb.api.utils.StringUtil;

/**
 * Spring MVC 配置
 */
@Configuration
public class WebMvcConfigurer extends WebMvcConfigurerAdapter {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
    }

    private final Logger logger = LoggerFactory.getLogger(WebMvcConfigurer.class);
    @DubboConsumer
    UserService userService;
    @Value("${spring.profiles.active}")
    private String env;//当前激活的配置文件

    //使用阿里 FastJson 作为JSON MessageConverter
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        FastJsonHttpMessageConverter4 converter = new FastJsonHttpMessageConverter4();
        FastJsonConfig config = new FastJsonConfig();

        config.setSerializerFeatures(
                SerializerFeature.WriteMapNullValue,//保留空的字段
                SerializerFeature.WriteNullStringAsEmpty,//String null -> ""
                SerializerFeature.WriteNullNumberAsZero,//Number null -> 0
                SerializerFeature.WriteNullListAsEmpty,//List null -> []
                SerializerFeature.WriteNullBooleanAsFalse //Boolean null -> false
        );
        converter.setFastJsonConfig(config);
        converter.setDefaultCharset(Charset.forName("UTF-8"));
        converters.add(converter);
    }

    @Override
    public void configureContentNegotiation(
            ContentNegotiationConfigurer configurer) {
        configurer.favorPathExtension(false);
    }
    //统一异常处理
    @Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
        exceptionResolvers.add(new HandlerExceptionResolver() {
            public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) {
                String uri = request.getRequestURI();
                uri = uri.replace("/" + Constants.BUILD_PATH + "/", "/");

                Result result = new Result();
                if (handler instanceof HandlerMethod) {
                    HandlerMethod handlerMethod = (HandlerMethod) handler;

                    if (e instanceof ServiceException) {//业务失败的异常，如“账号或密码错误”
                        result.setCode(ResultCode.FAIL).setMessage(e.getMessage());
                        logger.info(e.getMessage());
                    } else {
                        result.setCode(ResultCode.INTERNAL_SERVER_ERROR).setMessage("接口 [" + uri + "] 内部错误，请联系管理员");
                        String message = String.format("接口 [%s] 出现异常，方法：%s.%s，异常摘要：%s",
                                request.getRequestURI(),
                                handlerMethod.getBean().getClass().getName(),
                                handlerMethod.getMethod().getName(),
                                e.getMessage());
                        logger.error(message, e);
                    }
                } else {
                    if (e instanceof NoHandlerFoundException) {
                        result.setCode(ResultCode.NOT_FOUND).setMessage("接口 [" + uri + "] 不存在");
                    } else {
                        result.setCode(ResultCode.INTERNAL_SERVER_ERROR).setMessage(e.getMessage());
                        logger.error(e.getMessage(), e);
                    }
                }
                responseResult(response, result);
                return new ModelAndView();
            }

        });
    }

    //解决跨域问题
//    @Override
//    public void addCorsMappings(CorsRegistry registry) {
//        registry.addMapping("/**");
//    }

    //添加拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //接口签名认证拦截器，该签名认证比较简单，实际项目中建议使用Json Web Token代替。
        if (StringUtils.contains(env, "pro")) { //开发环境忽略签名认证
            registry.addInterceptor(new HandlerInterceptorAdapter() {

                @Override
                public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//                    logger.debug("[http]preHandle url: {"+request.getRequestURL()+"}, urlParams: {"+JSON.toJSONString(request.getQueryString())+"}" );
                    //跳过无须验证的
                    String uri = request.getRequestURI();
                    uri = uri.replace("/" + Constants.BUILD_PATH + "/", "/");
                    if(
                            uri.contains("/init/app")
                                    || (uri.contains("/test/list") && Constants.IS_DEBUG)
                                    || (uri.contains("/test/get") && Constants.IS_DEBUG)
                                    || (uri.contains("/test/success") && Constants.IS_DEBUG)
                                    || (uri.contains("/test/fail") && Constants.IS_DEBUG)
                                    || (uri.contains("/temp/user_info") && Constants.IS_DEBUG)
                                    || uri.contains("/route/nim.action")
                                    || uri.contains("/about/reg_agreement")
                                    || uri.contains("/pay/zfb_ybtz")
                                    || uri.contains("/public")
                                    || uri.contains("/file/upload")
                                    || uri.contains("/user/other_info")
                            ) {
                        return true;
                    }

                    String sign = request.getParameter("sign");
                    //验证签名
                    if (StringUtils.isNotEmpty(sign) && validateSign(request, sign)) {
                        //return true;

                        //验证登录
                        String validateLoginError = validateLogin(request);
                        if (StringUtils.isEmpty(validateLoginError)) {
                            return true;
                        } else {
                            logger.warn("用户认证失败，请求接口：{}，请求IP：{}，请求参数：{}",
                                    request.getRequestURI(), getIpAddress(request),
                                    JSON.toJSONString(request.getParameterMap()));

                            Result result = new Result();
                            result.setCode(ResultCode.LOGIN_FAILED).setMessage("用户认证失败：" +validateLoginError);
                            responseResult(response, result);
                            return false;
                        }
                    } else {
                        logger.warn("签名认证失败，请求接口：{}，请求IP：{}，请求参数：{}",
                                request.getRequestURI(), getIpAddress(request),
                                JSON.toJSONString(request.getParameterMap()));

                        Result result = new Result();
                        result.setCode(ResultCode.UNAUTHORIZED).setMessage("签名认证失败");
                        responseResult(response, result);
                        return false;
                    }
                }
            });
        }
    }

    private void responseResult(HttpServletResponse response, Result result) {
        response.setCharacterEncoding("UTF-8");
        response.setHeader("Content-type", "application/json;charset=UTF-8");
        response.setStatus(200);
        try {
            response.getWriter().write(JSON.toJSONString(result));
        } catch (IOException ex) {
            logger.error(ex.getMessage());
        }
    }

    //上传文件配置
    @Bean
    public MultipartConfigElement multipartConfigElement() {
        MultipartConfigFactory factory = new MultipartConfigFactory();
        // 设置文件大小限制 ,超出设置页面会抛出异常信息，
        // 这样在文件上传的地方就需要进行异常信息的处理了;
        // factory.setFileSizeThreshold("1024KB");
        factory.setMaxFileSize("160528KB"); // KB,MB
        /// 设置总上传数据总大小
        factory.setMaxRequestSize("160528KB");

        // Sets the directory location where files will be stored.
        // factory.setLocation("路径地址");
        return factory.createMultipartConfig();
    }

    /**
     * 一个简单的签名认证，规则：请求参数按ASCII码排序后，拼接为a=value&b=value...这样的字符串后进行MD5
     *
     * @param request
     * @param requestSign
     * @return
     */
    private boolean validateSign(HttpServletRequest request, String requestSign) {
        String uri = request.getRequestURI();
        uri = uri.replace("/" + Constants.BUILD_PATH + "/", "/");

        List<String> keys = new ArrayList<String>(request.getParameterMap().keySet());
        Collections.sort(keys);

        String linkString = "";
        if(!keys.contains("time") || !keys.contains("sign")) {
            return false;
        }
        if(StringUtil.isEmpty(request.getParameter("time")) || StringUtil.isEmpty(request.getParameter("sign"))){
            return false;
        }

        for (String key : keys) {
            if (!"time".equals(key) && !"sign".equals(key)) {
                //linkString += key + "=" + request.getParameter(key) + "&";
                linkString += request.getParameter(key);
            }
        }
        if (StringUtils.isEmpty(linkString)) {
            return false;
        }

        //linkString = linkString.substring(0, linkString.length() - 1);
        String time = request.getParameter("time");
        String key = "com.zzgele.qmzb";//自己修改
        String sign = DigestUtils.md5Hex(linkString + time + key);

        //logger.info("requestSign:{}, sign:{}", requestSign, sign);
        return StringUtils.equals(requestSign, sign);
    }

    /**
     * 登录验证
     * @param request
     * @return 错误信息
     */
    //TODO:转入Redis验证
    private String validateLogin(HttpServletRequest request) {
        String uri = request.getRequestURI();
        uri = uri.replace("/" + Constants.BUILD_PATH + "/", "/");

        //跳过无须验证的
        if(
                uri.equals("/sms/send")
                        || uri.equals("/user/login")
                        || uri.equals("/user/login_wechat")
                        || uri.equals("/user/login_qq")
                        || uri.equals("/user/login_weibo")
                ) {
            return "";
        }

        //开始验证
        Long login_uid = StringUtil.getLong(request.getParameter("login_uid"));

        if (login_uid <= 0) {
            return "用户认证参数异常";
        }
        String login_token = request.getParameter("login_token");
        if(StringUtil.isEmpty(login_token)) {
            return "用户认证参数异常!";
        }

        User user = userService.findById(login_uid);
        if(user == null) {
            return "uid或token错误";
        }
        if(user.getAccountStatus().equals(0)) {
            return "帐号已禁用";
        }
        if(!login_token.equals(user.getToken())) {
            return "登录已超时";
        }

        return "";
    }

    private String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        // 如果是多级代理，那么取第一个ip为客户ip
        if (ip != null && ip.indexOf(",") != -1) {
            ip = ip.substring(0, ip.indexOf(",")).trim();
        }

        return ip;
    }
}
